#include "Cuba.h"
#include "Registro.h"
#include "ClaveDatoCuba.h"
#include "DatoCuba.h"
#include "funciones.h"
#include "Excepciones.h"
#include "IndiceHashExtensible.h"

namespace Toolkit
{

Cuba::Cuba(const unsigned long int &tamanoBloque,
		const TipoSerializacion& tipoSerializacion) :
	Bloque(tamanoBloque, tipoSerializacion)
{
	this->profundidad = 0;
}

Cuba::~Cuba()
{
}

unsigned int Cuba::getProfundidad() const
{
	return this->profundidad;
}

void Cuba::incrementarProfundidad()
{
	profundidad++;
}

void Cuba::decrementarProfundidad()
{
	profundidad--;
}

bool Cuba::combinar(Cuba &cuba)
{
	Cuba cubaVacia(tamanoBloque, tSerializacion);
	unsigned long int tamanoCubaVacia = cubaVacia.getTamanoSerializacion();

	if (((long int) (cuba.getTamanoSerializacion() - tamanoCubaVacia))
			>= this->espacioLibre())
	{
		return false;
	}

    cuba.transferirElementosSinVerif(*this);

	this->decrementarProfundidad();

	return true;
}

void Cuba::setProfundidad(const unsigned int &profundidad)
{
    this->profundidad = profundidad;
}

void Cuba::agregar(Registro *nuevoComponente)
{
	DatoCuba *dato = (DatoCuba*) nuevoComponente->clonarDato();
	ClaveDatoCuba claveTemp(*dato, tSerializacion);

	delete dato;
	agregarSinChequearMaximo(nuevoComponente, claveTemp);
}

char Cuba::getTipo() const
{
	return 'C';
}

string Cuba::especializarSerializacion() const
{
	stringstream ss;

	serializacionDigito(ss, tSerializacion, profundidad);

	return ss.str();
}

void Cuba::especializarHidratacion(stringstream &serializacion)
{
	hidratacionDigito(serializacion, tSerializacion, profundidad);
}

DatoCuba* Cuba::getDatoCualquiera() const
{
	if (!componentes.empty())
	{
		Registro *aux = (Registro*) componentes.back();
		DatoCuba *datoAux = (DatoCuba*) aux->clonarDato();
		return datoAux;

	}
	else
	{
		return NULL;
	}
}

bool Cuba::puedeAgregar(const Dato& dato) const
{
	Registro registro(BINARIO);
	registro.setDato(dato);

	stringstream stream;
    registro.serializar(stream);

	return (this->espacioLibre() >= ((signed int)stream.str().size()));
}

void Cuba::transferirElementosSinVerif(Cuba &cuba)
{
	while (this->cantElementos() > 0)
	{
		Registro *registro = (Registro*)this->popBack();
		cuba.agregar(registro);
	}
}

Cuba* Cuba::dividir(const IndiceHashExtensible &indiceHash)
{
	Cuba *cubaNueva = new Cuba(this->tamanoBloque, BINARIO);
	Cuba cubaAux(this->tamanoBloque, BINARIO);

	this->incrementarProfundidad();
	cubaNueva->setProfundidad(this->getProfundidad());

	this->transferirElementosSinVerif(cubaAux);

	while (cubaAux.cantElementos() > 0)
	{
		Registro *registroAux = (Registro*)cubaAux.popBack();
		DatoCuba *datoAux = (DatoCuba*)registroAux->clonarDato();
		string claveHasheada = datoAux->getClaveHasheada();
		delete datoAux;

		unsigned long int direccion = indiceHash.crearDireccion(claveHasheada, this->getProfundidad());
		int primerBit = direccion & 1;

		if (primerBit == 0)
		{
			this->agregar(registroAux);
		} else {
			cubaNueva->agregar(registroAux);
		}
	}

	return cubaNueva;
}


Componente *Cuba::clonar() const
{
	Cuba *nueva = new Cuba(this->getTamanoBloque(), tSerializacion);

	nueva->setProfundidad(this->getProfundidad());
	for (list<Componente*>::const_iterator it = this->componentes.begin(); it
			!= componentes.end(); it++)
	{
		nueva->componentes.push_back((*it)->clonar());
	}

	return nueva;
}

}
